﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

namespace Deskopeia.Helper
{
#warning check whether this class is up-to-date (compare to wintools)
#warning remove unused stuff

    public static class DwmCompatiblePainting
    {
        #region Platform Invoke

        [StructLayout(LayoutKind.Sequential)]
        class RGBQUAD
        {
            public byte rgbBlue;
            public byte rgbGreen;
            public byte rgbRed;
            public byte rgbReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        class BITMAPINFOHEADER
        {
            public uint size;
            public int width;
            public int height;
            public ushort biPlanes;
            public ushort biBitCount;
            public uint biCompression;
            public uint biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public uint biClrUsed;
            public uint biClrImportant;
        }

        [StructLayout(LayoutKind.Sequential)]
        class BITMAPINFO
        {
            public BITMAPINFOHEADER bmiHeader;
            public RGBQUAD bmiColors;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct DTTOPTS
        {
            public int dwSize;
            public DTTOPSFlags dwFlags;
            public int crText;
            public int crBorder;
            public int crShadow;
            public int iTextShadowType;
            public Win32APIImports.POINT ptShadowOffset;
            public int iBorderSize;
            public int iFontPropId;
            public int iColorPropId;
            public int iStateId;
            public bool fApplyOverlay;
            public int iGlowSize;
            public int pfnDrawTextCallback;
            public IntPtr lParam;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct LOGFONTW
        {
            internal int lfHeight;
            internal uint lfWidth;
            internal uint lfEscapement;
            internal uint lfOrientation;
            internal uint lfWeight;
            internal byte lfItalic;
            internal byte lfUnderline;
            internal byte lfStrikeOut;
            internal byte lfCharSet;
            internal byte lfOutPrecision;
            internal byte lfClipPrecision;
            internal byte lfQuality;
            internal byte lfPitchAndFamily;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            internal string lfFaceName;
        }

        enum DTTOPSFlags : int
        {
            DTT_TEXTCOLOR = 0x0001,
            DTT_BORDERCOLOR = 0x0002,
            DTT_SHADOWCOLOR = 0x0004,
            DTT_SHADOWTYPE = 0x0008,
            DTT_SHADOWOFFSET = 0x0010,
            DTT_BORDERSIZE = 0x0020,
            DTT_FONTPROP = 0x0040,
            DTT_COLORPROP = 0x0080,
            DTT_STATEID = 0x0100,
            DTT_CALCRECT = 0x0200,
            DTT_APPLYOVERLAY = 0x0400,
            DTT_GLOWSIZE = 0x0800,
            DTT_CALLBACK = 0x1000,
            DTT_COMPOSITED = 0x2000
        }

        /// <summary>
        ///     Specifies a raster-operation code. These codes define how the color data for the
        ///     source rectangle is to be combined with the color data for the destination
        ///     rectangle to achieve the final color.
        /// </summary>
        enum TernaryRasterOperations : uint
        {
            /// <summary>dest = source</summary>
            SRCCOPY = 0x00CC0020,
            /// <summary>dest = source OR dest</summary>
            SRCPAINT = 0x00EE0086,
            /// <summary>dest = source AND dest</summary>
            SRCAND = 0x008800C6,
            /// <summary>dest = source XOR dest</summary>
            SRCINVERT = 0x00660046,
            /// <summary>dest = source AND (NOT dest)</summary>
            SRCERASE = 0x00440328,
            /// <summary>dest = (NOT source)</summary>
            NOTSRCCOPY = 0x00330008,
            /// <summary>dest = (NOT src) AND (NOT dest)</summary>
            NOTSRCERASE = 0x001100A6,
            /// <summary>dest = (source AND pattern)</summary>
            MERGECOPY = 0x00C000CA,
            /// <summary>dest = (NOT source) OR dest</summary>
            MERGEPAINT = 0x00BB0226,
            /// <summary>dest = pattern</summary>
            PATCOPY = 0x00F00021,
            /// <summary>dest = DPSnoo</summary>
            PATPAINT = 0x00FB0A09,
            /// <summary>dest = pattern XOR dest</summary>
            PATINVERT = 0x005A0049,
            /// <summary>dest = (NOT dest)</summary>
            DSTINVERT = 0x00550009,
            /// <summary>dest = BLACK</summary>
            BLACKNESS = 0x00000042,
            /// <summary>dest = WHITE</summary>
            WHITENESS = 0x00FF0062
        }

        public enum DrawTextFlags : int
        {
            DT_TOP = 0x00000000,
            DT_LEFT = 0x00000000,
            DT_CENTER = 0x00000001,
            DT_RIGHT = 0x00000002,
            DT_VCENTER = 0x00000004,
            DT_BOTTOM = 0x00000008,
            DT_WORDBREAK = 0x00000010,
            DT_SINGLELINE = 0x00000020,
            DT_EXPANDTABS = 0x00000040,
            DT_TABSTOP = 0x00000080,
            DT_NOCLIP = 0x00000100,
            DT_EXTERNALLEADING = 0x00000200,
            DT_CALCRECT = 0x00000400,
            DT_NOPREFIX = 0x00000800,
            DT_INTERNAL = 0x00001000,
            DT_EDITCONTROL = 0x00002000,
            DT_PATH_ELLIPSIS = 0x00004000,
            DT_END_ELLIPSIS = 0x00008000,
            DT_MODIFYSTRING = 0x00010000,
            DT_RTLREADING = 0x00020000,
            DT_WORD_ELLIPSIS = 0x00040000,
            DT_NOFULLWIDTHCHARBREAK = 0x00080000,
            DT_HIDEPREFIX = 0x00100000,
            DT_PREFIXONLY = 0x00200000,
        }

        [DllImport("user32.dll")]
        static extern int DrawText(IntPtr hDC, string lpString, int nCount, [In]ref Rectangle lpRect, uint uFormat);

        [DllImport("gdi32.dll")]
        static extern int SetBkMode(IntPtr hdc, int iBkMode);

        [DllImport("gdi32.dll")]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll")]
        static extern IntPtr CreateDIBSection(IntPtr hdc, [In, MarshalAs(UnmanagedType.LPStruct)] BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

        [DllImport("gdi32.dll")]
        static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        [DllImport("gdi32.dll")]
        static extern bool DeleteObject(IntPtr hObject);

        [DllImport("gdi32.dll")]
        static extern bool DeleteDC(IntPtr hdc);

        [DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr CreateFontIndirectW([In] ref LOGFONTW lplf);

        [DllImport("gdi32.dll")]
        static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

        [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        static extern int DrawThemeTextEx(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, string text, int iCharCount, int dwFlags, ref Win32APIImports.RECT pRect, ref DTTOPTS pOptions);

        [DllImport("uxtheme", CharSet = CharSet.Unicode)]
        static extern Int32 GetThemeSysFont(IntPtr hTheme, int iFontId, out LOGFONTW plf);

        [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr OpenThemeData(IntPtr hWnd, String classList);

        [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        static extern IntPtr CloseThemeData(IntPtr aHTheme);

        #endregion

        public static void DrawText(IntPtr hdc, string text, Rectangle rect, int fontSize, DrawTextFlags format, int glowSize, int xOffset, int yOffset)
        {
            bool dwmEnabled = DwmHelper.IsDwmCompositionEnabled();
            IntPtr hTheme = OpenThemeData(IntPtr.Zero, "CompositedWindow::Window");

            if (hTheme != IntPtr.Zero)
            {
                if (dwmEnabled)
                {
                    IntPtr hdcPaint = CreateCompatibleDC(hdc);
                    if (hdcPaint != IntPtr.Zero)
                    {
                        int cx = rect.Width;
                        int cy = rect.Height;

                        // Define the BITMAPINFO structure used to draw text.
                        // Note that biHeight is negative. This is done because
                        // DrawThemeTextEx() needs the bitmap to be in top-to-bottom
                        // order.
                        BITMAPINFO dib = new BITMAPINFO();
                        dib.bmiHeader = new BITMAPINFOHEADER();
                        dib.bmiHeader.size = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
                        dib.bmiHeader.width = cx;
                        dib.bmiHeader.height = -cy;
                        dib.bmiHeader.biPlanes = 1;
                        dib.bmiHeader.biBitCount = 32;
                        dib.bmiHeader.biCompression = 0/*BI_RGB*/;

                        IntPtr intp = IntPtr.Zero;
                        IntPtr hbm = CreateDIBSection(hdc, dib, 0/*DIB_RGB_COLORS*/, out intp, IntPtr.Zero, 0);
                        if (hbm != IntPtr.Zero)
                        {
                            IntPtr hbmOld = SelectObject(hdcPaint, hbm);

                            // Setup the theme drawing options.
                            DTTOPTS DttOpts = new DTTOPTS();
                            DttOpts.dwSize = Marshal.SizeOf(typeof(DTTOPTS));
                            DttOpts.dwFlags = DTTOPSFlags.DTT_COMPOSITED | DTTOPSFlags.DTT_GLOWSIZE;
                            DttOpts.iGlowSize = glowSize;

                            // Select a font.
                            LOGFONTW lgFont = new LOGFONTW();
                            IntPtr hFontOld = IntPtr.Zero;
                            if (GetThemeSysFont(hTheme, 801/*TMT_CAPTIONFONT*/, out lgFont) >= 0)
                            {
                                lgFont.lfHeight = -fontSize;
                                lgFont.lfQuality = 4;
                                IntPtr hFont = CreateFontIndirectW(ref lgFont);
                                hFontOld = SelectObject(hdcPaint, hFont);
                            }

                            // Draw the title.
                            Win32APIImports.RECT rcPaint = rect;
                            rcPaint.Top += yOffset;
                            rcPaint.Bottom += yOffset;
                            rcPaint.Left += xOffset;
                            rcPaint.Right += xOffset;
                            DrawThemeTextEx(hTheme, hdcPaint, 0, 0, text, -1, (int)format, ref rcPaint, ref DttOpts);

                            // Blit text to the frame.
                            BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, TernaryRasterOperations.SRCCOPY);

                            SelectObject(hdcPaint, hbmOld);

                            if (hFontOld != IntPtr.Zero)
                                SelectObject(hdcPaint, hFontOld);

                            DeleteObject(hbm);
                        }
                        DeleteDC(hdcPaint);
                    }
                }
                else
                {
                    // Select a font.
                    LOGFONTW lgFont = new LOGFONTW();
                    IntPtr hFontOld = IntPtr.Zero;
                    if (GetThemeSysFont(hTheme, 801/*TMT_CAPTIONFONT*/, out lgFont) >= 0)
                    {
                        lgFont.lfHeight = -fontSize;
                        IntPtr hFont = CreateFontIndirectW(ref lgFont);
                        hFontOld = SelectObject(hdc, hFont);
                    }

                    // Draw the title.
                    rect.X += xOffset;
                    rect.Y += yOffset;

                    SetBkMode(hdc, 1/*TRANSPARENT*/);
                    DrawText(hdc, text, -1, ref rect, (uint)format);

                    if (hFontOld != IntPtr.Zero)
                        SelectObject(hdc, hFontOld);
                }

                CloseThemeData(hTheme);
            }
        }
    }
}
